package org.absolutezero.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Version;


@Entity
@Table(name="recipe")
public class Recipe  implements Serializable{

    private static Logger logger = Logger.getLogger(User.class.getName());

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Version
    private Integer version;
    
    private String name;
    
    private Integer duration;
    
    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="author_id")
    private User author;
    
    @OneToMany(mappedBy = "recipe",cascade={CascadeType.ALL})
    private List<RecipeIngredient> ingredients;
    
    private Integer consumersNum;
        
    @Column(columnDefinition="text")
    private String text;
    
    @OneToMany(mappedBy = "recipe",cascade={CascadeType.ALL})
    private List<SearchResult> searches;
    
    public List<SearchResult> getSearches() {
		return searches;
	}

	public void setSearches(List<SearchResult> searches) {
		this.searches = searches;
	}

	public List<RecipeIngredient> getIngredients(){
    	return ingredients;
    }
    
    public void setIngredients(List<RecipeIngredient> ingredients){
    	this.ingredients = ingredients;
    }
    
    public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getDuration() {
		return duration;
	}

	public void setDuration(Integer duration) {
		this.duration = duration;
	}

	public User getAuthor() {
		return author;
	}

	public void setAuthor(User author) {
		this.author = author;
	}

	public Integer getConsumersNum() {
		return consumersNum;
	}

	public void setConsumersNum(Integer consumersNum) {
		this.consumersNum = consumersNum;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public Integer getVersion(){
 		return 0;
 	}
	
	
	@Override
 	public String toString(){
 		return String.format("Recipe[%d, %s, %s, %d %d]", id, name, duration,author.getId(),consumersNum );
 	}
	
	 private static EntityManager entityManager(){
     	return EMF.get().createEntityManager();
     }
	 
	 
    public void persist() {
        	
    		
    	    EntityManager em = entityManager();
    	    try{
    	    	
    	      EntityTransaction tx  = em.getTransaction();
     	      tx.begin();
     	  
     	      logger.info("Persisting recipe"); 
     	      Recipe r = em.merge(this);
     	      for(RecipeIngredient ri : r.getIngredients()){
     	    	  RecipeIngredient ri2 = em.merge(ri);
     	    	  ri2.setRecipe(r);
     	      }
    	      em.persist(r);
    	      tx.commit();
    	     
    	    } catch(Exception e){
    	    	logger.info(e.getMessage());
    	    }
    	    
    	    finally {
    	      em.close();
    	    }
    	  }

	public void remove() {
	    EntityManager em = entityManager();
	    try {
	      Recipe attached = em.find(Recipe.class, this.id);
	      EntityTransaction tx  = em.getTransaction();
 	      tx.begin();
	      em.remove(attached);
	      tx.commit();
	    } finally {
	      em.close();
	    }
	  }
	
	public static Recipe findRecipe(Long id){
 		if (id == null) {
 		      return null;
	    }
	    EntityManager em = entityManager();
	    try {
	      Recipe recipe = em.find(Recipe.class, id);
	      return recipe;
	    } finally {
	      em.close();
	    }
 	}
	
	public static List<Recipe> getUserRecipes(Long userId){
		EntityManager em = entityManager();
		Query q = em.createQuery("SELECT r FROM Recipe r WHERE r.author.id = :id");
		q.setParameter("id",userId);
		
		return (List<Recipe>)q.getResultList();
	}
	
	public static List<Recipe> getAllRecipes(){
		EntityManager em = entityManager();
		Query q = em.createQuery("SELECT r FROM Recipe r ");
		
		return (List<Recipe>)q.getResultList();
	}
	
	public static List<RecipeIngredient> getRecipeIngredients(Long recipeId){
		EntityManager em = entityManager();
		Query q = em.createQuery("SELECT ri FROM RecipeIngredient ri WHERE ri.recipe.id = :id");
		q.setParameter("id", recipeId);
		
		return (List<RecipeIngredient>) q.getResultList();
	}
	
	
	public static List<SearchResult> findMatchingRecipes(List<Shelf> shelves){
		
		EntityManager em = entityManager();
		
//		List<Product> products = new ArrayList<Product>();
		// map: global product id -> quantity of this product in fridge
		Map<Long, Long> productsIdMap = new HashMap<Long, Long>();
		for(Shelf s : shelves){
			Shelf shelf = em.merge(s);
			for(FridgeProduct fp : shelf.getFridgeProducts())
			{
//				Product p = fp.getProduct();
				Long pid = fp.getProduct().getId();
				Long quan = fp.getQuantity();
				
				Long baseQuan = 0L;
				
				if (productsIdMap.containsKey(pid))
				{
					baseQuan = productsIdMap.get(pid);
				}
				
				productsIdMap.put(pid, baseQuan + quan);
				
				System.out.println(">PUT>>> " + pid + "\t" + baseQuan + quan);
				
//				products.add(product);
			}
		}
		
		Query q = em.createQuery("SELECT r FROM Recipe r");
		List<Recipe> recipes = (List<Recipe>)q.getResultList();
//		List<Long> prodIds = new ArrayList<Long>();
//		for(Product p : products){
//			prodIds.add(p.getId());
//		}
		
		
		List<SearchResult> results = new ArrayList<SearchResult>();
		for(Recipe recipe : recipes){
			SearchResult result = null;
			int totalCost = 0;
			long totalQuantity = 0;
			long quanMatches = 0;
			
			List<RecipeIngredient> ingredientsList = recipe.getIngredients();
			
			for(RecipeIngredient ri : ingredientsList){
				totalCost += ri.getProduct().getPriceForUnit() * ri.getQuantity();
				totalQuantity += ri.getQuantity();
			}
			
			for(RecipeIngredient ri : ingredientsList)
			{
				long id = ri.getProduct().getId();
				long quan = ri.getQuantity();
				
//				if (prodIds.contains(id))
//				{
//					quanMatches += quan;
//				}
				
				if (productsIdMap.containsKey(id))
				{
					quanMatches += productsIdMap.get(id);
				}
			}
			
			if (quanMatches > 0)
			{
				result = new SearchResult();
				result.setRecipe(recipe);
				result.setTotalCost(totalCost);
				result.setMatchPercent((int)((float)quanMatches/(float)totalQuantity*100.0));
				results.add(result);
			}
			
		}
		return results;
	}
	
}
